﻿using FlyRemote.Base;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;

namespace FlyRemote.Remote
{
    public interface IProcessObserver
    {
        /// <summary>
        /// Recieves an update of the process that was opened.
        /// </summary>
        /// <param name="process">The process being opened.</param>
        void Update(Process process);
    }

    public interface IProcessesAssist
    {
        /// <summary>
        /// 订阅进程更改事件
        /// </summary>
        /// <param name="listener">The object that wants to listen to process update events.</param>
        void Subscribe(IProcessObserver listener);

        /// <summary>
        /// 取消进程更改消息通知
        /// </summary>
        /// <param name="listener">The object that wants to stop listening to process update events.</param>
        void Unsubscribe(IProcessObserver listener);

        /// <summary>
        /// 当前的进程
        /// </summary>
        Process OpenedProcess { get; set; }

        /// <summary>
        /// 获取所有进程
        /// </summary>
        /// <returns>An enumeration of see <see cref="ExternalProcess" />.</returns>
        IEnumerable<Process> GetProcesses();

        /// <summary>
        /// 取当前打开的进程
        /// </summary>
        Process GetOpenedProcess();




        /// <summary>
        /// Determines if the opened process is 32 bit.
        /// </summary>
        /// <returns>Returns true if the opened process is 32 bit, otherwise false.</returns>
        Boolean IsOpenedProcess32Bit();

        /// <summary>
        /// Determines if the opened process is 64 bit.
        /// </summary>
        /// <returns>Returns true if the opened process is 64 bit, otherwise false.</returns>
        Boolean IsOpenedProcess64Bit();

        /// <summary>
        /// Determines if this program is 32 bit.
        /// </summary>
        /// <returns>A boolean indicating if this program is 32 bit or not.</returns>
        Boolean IsSelf32Bit();

        /// <summary>
        /// Determines if this program is 64 bit.
        /// </summary>
        /// <returns>A boolean indicating if this program is 64 bit or not.</returns>
        Boolean IsSelf64Bit();

        /// <summary>
        /// Determines if a process is 32 bit.
        /// </summary>
        /// <param name="process">The process to check.</param>
        /// <returns>Returns true if the process is 32 bit, otherwise false.</returns>
        Boolean IsProcess32Bit(Process process);

        /// <summary>
        /// Determines if a process is 64 bit.
        /// </summary>
        /// <param name="process">The process to check.</param>
        /// <returns>Returns true if the process is 64 bit, otherwise false.</returns>
        Boolean IsProcess64Bit(Process process);

        /// <summary>
        /// Determines if the operating system is 32 bit.
        /// 当前系统是否32位
        /// </summary>
        /// <returns>A boolean indicating if the OS is 32 bit or not.</returns>
        Boolean IsOperatingSystem32Bit();

        /// <summary>
        /// Determines if the operating system is 64 bit.
        /// 当前系统是否64位
        /// </summary>
        /// <returns>A boolean indicating if the OS is 64 bit or not.</returns>
        Boolean IsOperatingSystem64Bit();
    }
    //// End interface
    public static class PAFactory
    {
        private static IProcessesAssist _process = ProcessAssistDefault.Instance;
        public static IProcessesAssist Default { get => _process; set { _process = value; } }
    }



    class ProcessAssistDefault : Singleton<ProcessAssistDefault>, IProcessesAssist
    {
        /// <summary>
        /// Thread safe collection of listeners.
        /// </summary>
        private ConcurrentHashSet<IProcessObserver> processListeners;

        /// <summary>
        /// A reference to target process.
        /// </summary>
        private Process openedProcess;

        /// <summary>
        /// Initializes a new instance of the <see cref="ProcessAssistDefault" /> class.
        /// </summary>
        private ProcessAssistDefault()
        {
            this.processListeners = new ConcurrentHashSet<IProcessObserver>();

            this.ListenForProcessDeath();
        }

        /// <summary>
        /// Gets a reference to the target process. This is an optimization to minimize accesses to the Processes component of the Engine.
        /// 这是一种优化，可以最小化对Engine的Processes组件的访问。
        /// </summary>
        public Process OpenedProcess
        {
            get
            {
                return this.openedProcess;
            }

            set
            {
                if (value != null)
                {
                }
                else if (this.OpenedProcess != null)
                {
                }

                this.openedProcess = value;

                if (this.processListeners != null)
                {
                    foreach (IProcessObserver listener in this.processListeners.ToList())
                    {
                        listener.Update(value);
                    }
                }
            }
        }

        /// <summary>
        /// Collection of process ids that have caused access issues.
        /// </summary>
        private static TtlCache<Int32> SystemProcessCache = new TtlCache<Int32>(TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(15));

        /// <summary>
        /// Collection of process ids for which an icon could not be fetched.
        /// </summary>
        private static TtlCache<Int32> NoIconProcessCache = new TtlCache<Int32>(TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(15));

        /// <summary>
        /// Collection of processes with a window.
        /// </summary>
        private static TtlCache<Int32> WindowedProcessCache = new TtlCache<Int32>(TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(15));

        /// <summary>
        /// Collection of processes without a window.
        /// </summary>
        private static TtlCache<Int32> NoWindowProcessCache = new TtlCache<Int32>(TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(5));

        /// <summary>
        /// Subscribes the listener to process change events.
        /// </summary>
        /// <param name="listener">The object that wants to listen to process update events.</param>
        public void Subscribe(IProcessObserver listener)
        {
            this.processListeners.Add(listener);

            if (this.OpenedProcess != null)
            {
                listener.Update(this.OpenedProcess);
            }
        }

        /// <summary>
        /// Unsubscribes the listener from process change events.
        /// </summary>
        /// <param name="listener">The object that wants to listen to process update events.</param>
        public void Unsubscribe(IProcessObserver listener)
        {
            this.processListeners?.Remove(listener);
        }

        /// <summary>
        /// Gets the process that has been opened.
        /// </summary>
        /// <returns>The opened process.</returns>
        public Process GetOpenedProcess()
        {
            return this.OpenedProcess;
        }

        /// <summary>
        /// Determines if the opened process is 32 bit.
        /// </summary>
        /// <returns>Returns true if the opened process is 32 bit, otherwise false.</returns>
        public Boolean IsOpenedProcess32Bit()
        {
            // First do the simple check if seeing if the OS is 32 bit, in which case the process wont be 64 bit
            if (this.IsOperatingSystem32Bit())
            {
                return true;
            }

            return this.IsProcess32Bit(this.OpenedProcess);
        }

        /// <summary>
        /// Determines if the opened process is 64 bit.
        /// </summary>
        /// <returns>Returns true if the opened process is 64 bit, otherwise false.</returns>
        public Boolean IsOpenedProcess64Bit()
        {
            return !this.IsOpenedProcess32Bit();
        }

        /// <summary>
        /// Gets all running processes on the system.
        /// </summary>
        /// <returns>An enumeration of see <see cref="Process" />.</returns>
        public IEnumerable<Process> GetProcesses()
        {
            return Process.GetProcesses();
        }

        /// <summary>
        /// Determines if the provided process is a system process.
        /// </summary>
        /// <param name="process">The process to check.</param>
        /// <returns>A value indicating whether or not the given process is a system process.</returns>
        public static Boolean IsProcessSystemProcess(Process process)
        {
            if (ProcessAssistDefault.SystemProcessCache.Contains(process.Id))
            {
                return true;
            }

            if (process.SessionId == 0 || process.BasePriority == 13)
            {
                ProcessAssistDefault.SystemProcessCache.Add(process.Id);
                return true;
            }

            try
            {
                if (process.PriorityBoostEnabled)
                {
                    // Accessing this field will cause an access exception for system processes. This saves
                    // time because handling the exception is faster than failing to fetch the icon later
                    return false;
                }
            }
            catch
            {
                ProcessAssistDefault.SystemProcessCache.Add(process.Id);
                return true;
            }

            return false;
        }


        private static IntPtr[] GetWindowHandlesForThread(Int32 threadHandle)
        {
            List<IntPtr> results = new List<IntPtr>();

            return results.ToArray();
        }


        /// <summary>
        /// Determines if this program is 32 bit
        /// </summary>
        /// <returns>A boolean indicating if this program is 32 bit or not</returns>
        public Boolean IsSelf32Bit()
        {
            return !Environment.Is64BitProcess;
        }

        /// <summary>
        /// Determines if this program is 64 bit
        /// </summary>
        /// <returns>A boolean indicating if this program is 64 bit or not</returns>
        public Boolean IsSelf64Bit()
        {
            return Environment.Is64BitProcess;
        }

        /// <summary>
        /// Determines if a process is 32 bit
        /// </summary>
        /// <param name="process">The process to check</param>
        /// <returns>Returns true if the process is 32 bit, otherwise false</returns>
        public Boolean IsProcess32Bit(Process process)
        {
            Boolean isWow64;

            // First do the simple check if seeing if the OS is 32 bit, in which case the process wont be 64 bit
            if (this.IsOperatingSystem32Bit())
            {
                return true;
            }

            // No process provided, assume 32 bit
            if (process == null)
            {
                return true;
            }

            try
            {
                if (this.OpenedProcess == null || !NativeMethods.IsWow64Process(this.OpenedProcess.Handle, out isWow64))
                {
                    // Error, assume 32 bit
                    return true;
                }
            }
            catch
            {
                // Error, assume 32 bit
                return true;
            }

            return isWow64;
        }

        /// <summary>
        /// Determines if a process is 64 bit
        /// </summary>
        /// <param name="process">The process to check</param>
        /// <returns>Returns true if the process is 64 bit, otherwise false</returns>
        public Boolean IsProcess64Bit(Process process)
        {
            return !this.IsProcess32Bit(process);
        }

        /// <summary>
        /// Determines if the operating system is 32 bit
        /// </summary>
        /// <returns>A boolean indicating if the OS is 32 bit or not</returns>
        public Boolean IsOperatingSystem32Bit()
        {
            return !Environment.Is64BitOperatingSystem;
        }

        /// <summary>
        /// Determines if the operating system is 64 bit
        /// </summary>
        /// <returns>A boolean indicating if the OS is 64 bit or not</returns>
        public Boolean IsOperatingSystem64Bit()
        {
            return Environment.Is64BitOperatingSystem;
        }

        /// <summary>
        /// Listens for process death and detaches from the process if it closes.
        /// </summary>
        private void ListenForProcessDeath()
        {
            Task.Run(async () =>
            {
                while (true)
                {
                    try
                    {
                        // DbgPlugin  使用 DbgPlugin 打开， 会造成判断失败 
                        // 误以为 进行已关闭
                        if (this.OpenedProcess?.HasExited ?? false)
                        {
                            // this.OpenedProcess = null;
                        }
                    }
                    catch
                    {
                    }

                    await Task.Delay(200);
                }
            });
        }
    }


}
